package pt.inescid.components.policyengine.heimdall.pdp.base;

import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.commons.Log;
import pt.inescid.components.policyengine.heimdall.pap.base.PolicyDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.QuantifierDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.RuleDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.SimpleDefinition;
import pt.inescid.components.policyengine.heimdall.pdp.PolicyDecision;

public class PolicyInstance {

	public static final int RELEVANCE_SECTION = 0;
	public static final int DECISION_SECTION = 1;

	private Vector quantifiers;
	private RuleInstance relevance;
	private RuleInstance authorization;
	private RuleInstance obligedAuthorization;
	
	private PolicyDecision decision;

	private PolicyDefinition definition;
	
	
//  ----------------------------------------------------------
//  ------------------------   constructor   ------------------------
//  ----------------------------------------------------------
	public PolicyInstance(PolicyDefinition definition, 
			ModelInstance currentModel) {
		this.definition = definition;
		Vector quantDef = definition.getQuantifiers();
		
		quantifiers = new Vector();
		for (int i = 0; i < quantDef.size(); i++) {
			quantifiers.add(new QuantifierInstance((QuantifierDefinition) 
					quantDef.elementAt(i), currentModel, this));
		}
		
		QuantifierInstance currentQt = null;
		QuantifierInstance nextQt = null;
		for (int i = quantifiers.size()-1; i > 0; i--) {
			nextQt = (QuantifierInstance) quantifiers.elementAt(i);
			currentQt = (QuantifierInstance) quantifiers.elementAt(
					i-1);
			
			currentQt.setNextQt(nextQt);
		}
		
		relevance = RuleInstance.getInstance(definition.getRelevance(),
				currentModel, this);
		
		if (definition.isObligation()) {
			authorization = RuleInstance.getInstance(
					definition.getAuthorization().
					getObligationIndependentCopy(),
					currentModel, this);
			
			if (authorization == null) {
				authorization = new SimpleInstance(
						new SimpleDefinition(
								RuleDefinition.LITERAL_EXPRESSION,
								RuleDefinition.EXPRESSION_VALUE_CODES[
								           RuleDefinition.BOOLEAN_VALUE],
								null,
								"true"), 
						this);
			}

			obligedAuthorization = RuleInstance.getInstance(
					definition.getAuthorization().getObligation(),
					currentModel,
					this);
		}
		else if (definition.getAuthorization() != null) {
			authorization = RuleInstance.getInstance(
					definition.getAuthorization(),
					currentModel, this);
		}
		
		decision = new PolicyDecision();
	}

	

	/* ===========================================================
	==============================================================
	=============      evaluate       =============================
	==============================================================
	=========================================================== */
	public void evaluate(ModelInstance currentModel, 
			boolean onlyCached) {
		Log.debug(Log.INFO, "Arrived.", "PolicyInstance.evaluate");
		
		// evaluate the quantifiers,
		// or the policy itself it it has no quantifiers
		QuantifierInstance masterQt = null;
		if (currentModel.getDecision().getStatus() !=
			PolicyDecision.STATUS_WAIT_OBLIG &&
			currentModel.getDecision().getStatus() !=
				PolicyDecision.STATUS_WAIT_OBLIG_INFO) {
			
			QuantifierInstance currentQt = null;
			for (int i = 0; i < quantifiers.size(); i++) {
				currentQt = (QuantifierInstance) quantifiers.elementAt(i);
				
				if (!currentQt.getDefinition().isObligation()) {
					
					masterQt = currentQt;
					i = quantifiers.size();
				}	
			}
		}
		else {
			masterQt = (QuantifierInstance) quantifiers.elementAt(0);
		}

			
		if (masterQt == null) {
			evaluateRule(currentModel, onlyCached);
		}
		else {
			masterQt.evaluate(currentModel);
			decision = masterQt.getDecision();
		}
	}; // evaluate
	
	

	
	
	
	/* ===========================================================
	==============================================================
	=============      evaluateRule       =============================
	==============================================================
	=========================================================== */
	public void evaluateRule(ModelInstance currentModel, boolean onlyCached) {
		Log.debug(Log.INFO, "Arrived.", "PolicyInstance.evaluate");
		
		PolicyDecision subDecision = new PolicyDecision();
		RuleInstance authzRule = null;
		
		// if we're waiting for an obligation fulfillment,
		// test the obligedAuthorization Rule.
		// Else test the original policy rules
		if (currentModel.getDecision().getStatus() ==
				PolicyDecision.STATUS_WAIT_OBLIG) {
			
			authzRule = obligedAuthorization;
		}
		else {
			authzRule = authorization;
		}
		
		// if it's a policy composition, evaluate the composition, 
		// else evaluate the current policy
		if (authorization.getDefinition().isPolicyComposition()) {
			Vector allPolicies = null;
			
			if (authorization.getDefinition().isBooleanExpression()) {
					allPolicies = ((CompositeInstance) authorization).getOperands();
			}
			else {
				allPolicies = new Vector();
				allPolicies.add(authorization);
			}
			
			SubPolicyInstance currentPolicy = null;
			decision.setStatus(PolicyDecision.STATUS_UNDEFINED);
			
			for (int i = 0; i < allPolicies.size(); i++) {
				currentPolicy = (SubPolicyInstance) allPolicies.elementAt(i);
				
				currentPolicy.evaluate(currentModel, onlyCached);
				subDecision = currentPolicy.getDecision();
				if (subDecision.getValue() == 
					PolicyDecision.DECISION_DENY) {
					
					decision.setValue(PolicyDecision.DECISION_DENY);
					decision.setStatus(PolicyDecision.STATUS_FINAL);
					
					return;
				}
				else if (subDecision.getStatus() == 
					PolicyDecision.STATUS_WAITING_INFO) {
					
					decision.setStatus(
							PolicyDecision.STATUS_WAITING_INFO);
				}
			}
			
			if (decision.getStatus() != 
					PolicyDecision.STATUS_WAITING_INFO) {
				decision.setStatus(PolicyDecision.STATUS_FINAL);
				decision.setValue(PolicyDecision.DECISION_AUTHORIZE);
			}
			return;
		}
		else {
			evaluateIteration(currentModel, authzRule, onlyCached);
		}
	}; // evaluateRule
	
	

	
	
	/* ===========================================================
	==============================================================
	=============      evaluateIteration       =============================
	==============================================================
	=========================================================== */
	public void evaluateIteration(ModelInstance currentModel,
			RuleInstance authzRule,
			boolean onlyCached) {
		Log.debug(Log.INFO, "Arrived.", "PolicyInstance.evaluateIteration");

		PolicyDecision subDecision = new PolicyDecision();

		// if relevance is denied than this policy is NOT APPLICABLE
		// else its value is that of the authorization rule.
		
		currentModel.getDecision().setStatus(
				PolicyDecision.STATUS_UNDEFINED);
		
		if (((Boolean) relevance.evaluate(
				currentModel, onlyCached)).booleanValue()) {
			subDecision.setValue(PolicyDecision.EVALUATION_RELEVANT);
		}
		else {
			subDecision.setValue(PolicyDecision.EVALUATION_IRRELEVANT);
		}
		
//		subDecision.setStatus(currentModel.getDecision().getStatus());
		
		if ((subDecision.getStatus() == PolicyDecision.STATUS_FINAL ||
			subDecision.getStatus() == PolicyDecision.STATUS_UNDEFINED) && 
				subDecision.getValue() == PolicyDecision.EVALUATION_IRRELEVANT) {
			
			subDecision.setValue(PolicyDecision.DECISION_NOT_APPLICABLE);
			subDecision.setStatus(PolicyDecision.STATUS_FINAL);
			decision = subDecision;
			return;
		}
		else if (subDecision.getStatus() == 
			PolicyDecision.STATUS_WAITING_INFO) {

			return;
		}
		
		if (((Boolean) authzRule.evaluate(
				currentModel, onlyCached)).booleanValue()) {
			subDecision.setValue(PolicyDecision.DECISION_AUTHORIZE);
		}
		else {
			subDecision.setValue(PolicyDecision.DECISION_DENY);
		}
		
		subDecision.setStatus(currentModel.getDecision().getStatus());
		
		decision = subDecision;

	} // evaluateIteration
	

	
//  ----------------------------------------------------------
//  ------------------------   toString   ------------------------
//  ----------------------------------------------------------
	public String toString() {
		String result = "";
		
		result += "Qt;\n";
		for (int i = 0; i < quantifiers.size(); i++) {
			result += ((QuantifierInstance) quantifiers.elementAt(i)) + "\n";
		}
		
		result += "Relev:\n" + relevance + "\n";
		result += "Authz:\n" + authorization + "\n";
		result += "Decis:\n" + decision + "\n";
		
		return (result);
	}



	public RuleInstance getAuthorization() {
		return authorization;
	}



	public PolicyDecision getDecision() {
		return decision;
	}



	public PolicyDefinition getDefinition() {
		return definition;
	}



	public Vector getQuantifiers() {
		return quantifiers;
	}



	public RuleInstance getRelevance() {
		return relevance;
	}



	public void setAuthorization(RuleInstance authorization) {
		this.authorization = authorization;
	}



	public void setDecision(PolicyDecision decision) {
		this.decision = decision;
	}



	public void setQuantifiers(Vector quantifiers) {
		this.quantifiers = quantifiers;
	}



	public void setRelevance(RuleInstance relevance) {
		this.relevance = relevance;
	}


}; // PolicyDefinition
